/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
 * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see
 * <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.binding.guicomponents.lists;

import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import org.jdesktop.observablecollections.*;
import ch.trackedbean.binding.guicomponents.action.*;

/**
 * Adapter for {@link JList}s.<br>
 * You may use this mapper for selection/content dependent actions.<br>
 * If {@link ISelectionAction#selectionChanged(List)} returns false then {@link Action#setEnabled(boolean)} will be
 * called with <code>false</code> as parameter.
 * 
 * @param <C> The content type of the list
 * 
 * @author M. Hautle
 */
public class JListAdapter<C> implements IListAdapter<JList, C>, ObservableListListener, ListSelectionListener, MouseListener {
	/** The registred selection actions. */
	protected final List<ISelectionAction> selectionActions = new ArrayList<ISelectionAction>(5);

	/** The popupmanager. */
	protected LocalPopupManager popupManager = new LocalPopupManager();

	/** The popup. */
	protected JPopupMenu popup = new JPopupMenu();

	/** The list to wrap. */
	protected final JList list;

	/** The list representing the model. */
	protected List<C> model;

	/** The default action or null. */
	protected ISelectionAction<C> defaultAction;

	/**
	 * Default constructor.
	 * 
	 * @param list The list to wrap
	 */
	public JListAdapter(JList list) {
		this.list = list;
		list.addListSelectionListener(this);
		list.addMouseListener(this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public JList getComponent() {
		return list;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setModel(List<C> model) {
		if (this.model instanceof ObservableList)
			((ObservableList) this.model).removeObservableListListener(this);
		if (model instanceof ObservableList)
			((ObservableList) model).addObservableListListener(this);
		this.model = model;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public List<C> getModel() {
		return model;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	@SuppressWarnings("unchecked")
	public List<C> getSelection() {
		final Object[] sel = list.getSelectedValues();
		if (sel.length == 0)
			return Collections.emptyList();
		return (List<C>) Arrays.asList(sel);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int[] getSelectedIndices() {
		return list.getSelectedIndices();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void listElementPropertyChanged(ObservableList list, int index) {
		fireEvent();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void listElementReplaced(ObservableList list, int index, Object oldElement) {
		fireEvent();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void listElementsAdded(ObservableList list, int index, int length) {
		fireEvent();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void listElementsRemoved(ObservableList list, int index, List oldElements) {
		fireEvent();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void valueChanged(ListSelectionEvent e) {
		if (!e.getValueIsAdjusting())
			fireEvent();
	}

	/**
	 * Fires a list selection event.
	 */
	protected void fireEvent() {
		final List<C> sel = getSelection();
		for (ISelectionAction<C> a : selectionActions)
			a.setEnabled(a.selectionChanged(sel));
	}

	/**
	 * Sets the default action of the list.
	 * 
	 * @param action The action or null
	 */
	public void setDefaultAction(ISelectionAction<C> action) {
		this.defaultAction = action;
	}

	/**
	 * Adds the given action to the popup.
	 * 
	 * @param action The action
	 */
	public void addLocalAction(ISelectionAction<C> action) {
		popupManager.addLocalAction(action);
	}

	/**
	 * Adds the given action to the popup at the given location.
	 * 
	 * @param path The location where to add the action
	 * @param action The action
	 */
	public void addLocalAction(String path, ISelectionAction<C> action) {
		popupManager.addLocalAction(path, action);
	}

	/**
	 * Removes the given action from the popup.
	 * 
	 * @param action The action to remove
	 * @return True if the action was removed
	 */
	public boolean removeLocalAction(ISelectionAction action) {
		return popupManager.removeLocalAction(action);
	}

	/**
	 * @return Returns the popupManager.
	 */
	public LocalPopupManager getPopupManager() {
		return popupManager;
	}

	/**
	 * Shows the popup for the selection.
	 * 
	 * @param e The mouse event
	 */
	protected void showPopup(MouseEvent e) {
		popupManager.populate(popup, getSelection());
		if (popup.getComponentCount() > 0)
			popup.show(e.getComponent(), e.getX(), e.getY());
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void addAction(ISelectionAction<C> action) {
		if (selectionActions.contains(action))
			return;
		selectionActions.add(action);
		action.setEnabled(action.selectionChanged(getSelection()));
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void removeAction(ISelectionAction<C> action) {
		selectionActions.remove(action);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void dispose() {
		list.removeListSelectionListener(this);
		if (list instanceof ObservableList)
			((ObservableList) list).removeObservableListListener(this);
		selectionActions.clear();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void mouseClicked(MouseEvent e) {
		if (defaultAction == null || e.getClickCount() < 2)
			return;
		final List<C> sel = getSelection();
		if (sel.size() != 1)
			return;
		if (defaultAction.selectionChanged(sel))
			defaultAction.actionPerformed(new ActionEvent(getComponent(), 0, ""));
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void mousePressed(MouseEvent e) {
		if (e.isPopupTrigger())
			showPopup(e);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void mouseReleased(MouseEvent e) {
		if (e.isPopupTrigger())
			showPopup(e);
	}

	/**
	 * Not implemented.
	 * 
	 * @param e Not used
	 */
	@Override
	public void mouseEntered(MouseEvent e) {
	}

	/**
	 * Not implemented.
	 * 
	 * @param e Not used
	 */
	@Override
	public void mouseExited(MouseEvent e) {
	}
}
